#ifndef UTIL_SMART_PTR_H
#define UTIL_SMART_PTR_H

#include <cassert>

namespace util
{

/**
 * A smart pointer class that provides a reference counting and auto delete memory.
 *
 * This class is similar to std::auto_ptr, with 2 exceptions:
 * - This class uses reference counting
 * - We dont provide a release() function (because of the reference counting)
 * It is recommended to use this class instead of using raw pointer wherever possible.
 *
 * \note smart pointer to NULL is valid.
 * \date 09-17-2006
 * \author Eran
 */
template <typename T>
class shared_ptr
{
	typedef T* type_ptr;

	/**
     * The reference counting class
     *
	 * \ingroup CodeLite
	 * \version 1.0
	 * first version
	 *
	 * \date 09-17-2006
	 * \author Eran
	 */
	class shared_ptr_ref
	{
		type_ptr m_data;
		int m_refCount;

	public:
		/**
		 * Construct a reference counting class for row pointer data
		 * \param data pointer
		 */
		shared_ptr_ref(type_ptr data)
			: m_data(data), m_refCount(1)
		{
		}

		/**
		 * Destructor
		 */
		virtual ~shared_ptr_ref()
		{
			delete m_data;
		}

		/**
		 * \return Pointer to the row data 
		 */
		type_ptr get_data() { return m_data; }

		/**
		 * Increase reference counting by 1
		 */
		void inc_ref() { ++m_refCount; }

		/**
		 * Decrease reference counting by 1
		 */
		void dec_ref() { --m_refCount; }
		
		/**
		 * Return the current reference counting
		 * \return current reference counting
		 */
		int  get_ref_count() { return m_refCount; }
	};

	shared_ptr_ref *m_ref;

public:
	/**
	 * Construct smart pointer from ptr
	 * \param ptr pointer
	 */
	shared_ptr(type_ptr ptr)
	{
		// create a fresh copy
		create_fresh( ptr );
	}
	
	/**
	 * Default constructor
	 */
	shared_ptr()
		: m_ref(0)
	{
	}

	/**
	 * Copy constructor
	 * \param rhs right hand side 
	 */
	shared_ptr(const shared_ptr& rhs)
		: m_ref(0)
	{
		*this = rhs;
	}

	/**
	 * Assignment operator
	 * \param rhs right hand side 
	 * \return reference to this
	 */
	shared_ptr& operator=(const shared_ptr& rhs)
	{
		// increase the reference count
		if (m_ref == rhs.m_ref)
			return *this;

		// delete previous reference 
		delete_ref_count();

		if (!rhs.m_ref)
			return *this;

		m_ref = rhs.m_ref;
		m_ref->inc_ref();
		
		return *this;
	}

	/**
	 * Destructor
	 */
	virtual ~shared_ptr()
	{
		delete_ref_count();
	}

	/**
	 * Replace the current pointer with ptr
	 * if the current ptr is not NULL, it will be freed (reference counting free) before assingning the new ptr
	 * \param ptr new pointer
	 */
	void reset(type_ptr ptr)
	{	
		delete_ref_count();
		create_fresh(ptr);
	}

	/**
	 * Return pointer the row data pointer
	 * \return pointer to the row data pointer
	 */
	type_ptr get()
	{
		return m_ref->get_data();
	}

	/**
	 * Overload the '->' operator 
	 * \return pointer to the row data pointer
	 */
	type_ptr operator->() const 
	{
		return m_ref->get_data();
	}

	/**
	 * Dereference operator
	 * \return dereference the row data
	 */
	T& operator*() const
	{
		return *(m_ref->get_data());
	}

	/**
	 * Test for NULL operator
	 * \return true if the internal row data or the reference counting class are NULL false otherwise
	 */
	bool operator!() const
	{
		if (!m_ref)
			return true;

		return m_ref->get_data() == 0;
	}

private:
	void delete_ref_count()
	{
		// decrease the ref count (or delete pointer if it is 1)
		if (m_ref)
		{
			if (m_ref->get_ref_count() == 1)
			{
				delete m_ref;
				m_ref = 0;
			}
			else
				m_ref->dec_ref();
		}
	};

	void create_fresh(type_ptr ptr)
	{
		m_ref = new shared_ptr_ref(ptr);
	}
};


/// Verify that types are complete for increased safety
/// From <boost/checked_delete.h>
template<class T> inline void checked_delete(T* x)
{
	// Intentionally complex - simplification causes regressions
	typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
	(void) sizeof(type_must_be_complete);
	delete x;
}


/**
   The scoped_ptr class stores a pointer to a dynamically allocated object. 
   (Dynamically allocated objects are allocated with the C++ new expression.) 
   The object pointed to is guaranteed to be deleted, either on destruction of the scoped_ptr, 
   or via an explicit reset.

   The scoped_ptr template is a simple solution for simple needs. 
   It supplies a basic "resource acquisition is initialization" facility, 
   without shared-ownership or transfer-of-ownership semantics. 
   Both its name and enforcement of semantics (by being noncopyable) signal its intent 
   to retain ownership solely within the current scope. 
   Because it is noncopyable, it is safer than shared_ptr or std::auto_ptr for pointers 
   which should not be copied.
*/
template<class T>
class scoped_ptr // noncopyable
{
private:

    T* ptr;

    scoped_ptr(scoped_ptr const &);
    scoped_ptr& operator=(scoped_ptr const &);

    typedef scoped_ptr<T> this_type;

public:

    typedef T element_type;

    explicit scoped_ptr(T * p = 0): ptr(p) // never throws
    {
    }

    ~scoped_ptr() // never throws
    {
        checked_delete(ptr);
    }

    void reset(T * p = 0) // never throws
    {
        assert(p == 0 || p != ptr); // catch self-reset errors
        this_type(p).swap(*this);
    }

    T& operator*() const // never throws
    {
        assert(ptr != 0);
        return *ptr;
    }

    T* operator->() const // never throws
    {
        assert(ptr != 0);
        return ptr;
    }

    T* get() const // never throws
    {
        return ptr;
    }

    // implicit conversion to "bool"

    typedef T * this_type::*unspecified_bool_type;

    operator unspecified_bool_type() const // never throws
    {
        return ptr == 0 ? 0: &this_type::ptr;
    }

    bool operator! () const // never throws
    {
        return ptr == 0;
    }

    void swap(scoped_ptr & b) // never throws
    {
        T* tmp = b.ptr;
        b.ptr = ptr;
        ptr = tmp;
    }
};

template<class T> inline void swap(scoped_ptr<T>& a, scoped_ptr<T>& b) // never throws
{
    a.swap(b);
}

// get_pointer(p) is a generic way to say p.get()

template<class T> inline T * get_pointer(scoped_ptr<T> const & p)
{
    return p.get();
}








}

#endif // SMART_PTR_H
